#include "pch.h"
#include "cimagehelper.h"

#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmath>
#include <chrono>

extern "C" {
#ifdef USE_JPEG_LIB
    #include <jpeglib.h>
#endif

#ifdef USE_TURBO_JPEG
    #include <turbojpeg.h>
#endif
}


namespace libtool_sdk
{
#pragma pack(push)
#pragma pack(1)      //Two bytes are aligned, otherwise BMP_FILEHEADER will accounted for 16bytete
    struct bmp_fileheader
    {
        uint16_t    bfType;        //If you are not aligned, this will account for 4BYTE
        uint32_t    bfSize;
        uint16_t    bfReverved1;
        uint16_t    bfReverved2;
        uint32_t    bfOffBits;
    };

    struct bmp_infoheader
    {
        uint32_t    biSize;
        uint32_t    biWidth;
        uint32_t    biHeight;
        uint16_t    biPlanes;
        uint16_t    biBitCount;
        uint32_t    biCompression;
        uint32_t    biSizeImage;
        uint32_t    biXPelsPerMeter;
        uint32_t    biYpelsPerMeter;
        uint32_t    biClrUsed;
        uint32_t    biClrImportant;
    };
#pragma pack(pop)

void debug_logfmt(const char* szfmt, ...)
{
    char chLog_buf[512] = { 0 };
    memset(chLog_buf, 0, sizeof(chLog_buf));

    va_list arg_ptr;
    va_start(arg_ptr, szfmt);
    vsnprintf(chLog_buf, sizeof(chLog_buf), szfmt, arg_ptr);
    va_end(arg_ptr);

    auto now = std::chrono::system_clock::now();
    time_t in_time_t = std::chrono::system_clock::to_time_t(now);
    auto duration = now.time_since_epoch();
    int   iMillisecond = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
    struct tm* ptmNow = localtime(&in_time_t);

    char chFileName[256] = { 0 };
    sprintf(chFileName, "%04d%02d%02d_imagehelper.log",
        ptmNow->tm_year + 1900,
        ptmNow->tm_mon + 1,
        ptmNow->tm_mday);

    FILE *file = NULL;
    file = fopen(chFileName, "a+");
    //fopen_s(&file, chLogFileName, "a+");
    if (file)
    {
        fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d.%03d] %s\n",
            ptmNow->tm_year + 1900,
            ptmNow->tm_mon + 1,
            ptmNow->tm_mday,
            ptmNow->tm_hour,
            ptmNow->tm_min,
            ptmNow->tm_sec,
            iMillisecond,
            chLog_buf);

        fclose(file);
        file = NULL;
    }
}


// #define OUT_LOG(fmt,...) fprintf(stderr,  "%s:: " fmt "\n", __FUNCTION__, ##__VA_ARGS__);
#define OUT_LOG(fmt,...) debug_logfmt("%s:: " fmt, __FUNCTION__, ##__VA_ARGS__);

TOOL_SDK_API int DLL_SPEC simplest_bgr24_to_bmp(unsigned char* pSrcBuffer, int iBufferLen, int width, int height, unsigned char* pDestBuffer, int*OutPutLength)
{
    typedef struct
    {
        int32_t imageSize;
        int32_t blank;
        int32_t startPosition;
    }BmpHead;

    typedef struct
    {
        int32_t  Length;
        int32_t  width;
        int32_t  height;
        uint16_t  colorPlane;
        uint16_t  bitColor;
        int32_t  zipFormat;
        int32_t  realSize;
        int32_t  xPels;
        int32_t  yPels;
        int32_t  colorUse;
        int32_t  colorImportant;
    }InfoHead;

    //int i = 0, j = 0;
    BmpHead m_BMPHeader = { 0, 0, 0 };

    InfoHead  m_BMPInfoHeader = { 0 , 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    char bfType[2] = { 'B', 'M' };
    int header_size = sizeof(bfType) + sizeof(BmpHead) + sizeof(InfoHead);
    unsigned char *rgb24_buffer = pSrcBuffer;
    unsigned char *pDest = pDestBuffer;

    m_BMPHeader.imageSize = 3 * width*height + header_size;
    m_BMPHeader.startPosition = header_size;

    m_BMPInfoHeader.Length = sizeof(InfoHead);
    m_BMPInfoHeader.width = width;
    //BMP storage pixel data in opposite direction of Y-axis (from bottom to top).
    m_BMPInfoHeader.height = -height;
    m_BMPInfoHeader.colorPlane = 1;
    m_BMPInfoHeader.bitColor = 24;
    m_BMPInfoHeader.realSize = 3 * width*height;

    memcpy(pDest, bfType, sizeof(bfType));
    pDest += sizeof(bfType);
    memcpy(pDest, &m_BMPHeader, sizeof(m_BMPHeader));
    pDest += sizeof(m_BMPHeader);
    memcpy(pDest, &m_BMPInfoHeader, sizeof(m_BMPInfoHeader));
    pDest += sizeof(m_BMPInfoHeader);

    *OutPutLength = 3 * width*height + sizeof(bfType) + sizeof(m_BMPHeader) + sizeof(m_BMPInfoHeader);

//    BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2
//    It saves pixel data in Little Endian
//    So we change 'R' and 'B'
    for (int j = 0; j<height; j++)
    {
        for (int i = 0; i<width; i++)
        {
            char temp = rgb24_buffer[(j*width + i) * 3 + 2];
            rgb24_buffer[(j*width + i) * 3 + 2] = rgb24_buffer[(j*width + i) * 3 + 0];
            rgb24_buffer[(j*width + i) * 3 + 0] = temp;
        }
    }

    memcpy(pDest, rgb24_buffer, iBufferLen);

    return 0;
}

#ifdef USE_JPEG_LIB

// 定义一个自定义的错误处理结构体
struct my_error_mgr 
{
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};

// 自定义错误处理函数
METHODDEF(void) my_error_exit(j_common_ptr cinfo) 
{
    struct my_error_mgr *myerr = (struct my_error_mgr *)cinfo->err;
    (*cinfo->err->output_message)(cinfo);
    longjmp(myerr->setjmp_buffer, 1);
}

TOOL_SDK_API int DLL_SPEC jpeg_decompress_to_rgb24(unsigned char *pjpgBuffer, int jpgSize, void **pRGBBuffer, unsigned long *pOutSize, int *outWidth, int *outHeigth)
{
    if (pjpgBuffer[0] != 0xFFu || pjpgBuffer[1] != 0xD8u)
    {
        OUT_LOG("Bad jpeg start with: %#x%x", pjpgBuffer[0], pjpgBuffer[1]);
        return -1;
    }

    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    OUT_LOG("jpeg_create_decompress");
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo, pjpgBuffer, jpgSize);

    OUT_LOG("jpeg_read_header");
    int rc = jpeg_read_header(&cinfo, true);
    if (rc != 1)
    {
        OUT_LOG( "File does not seem to be a normal JPEG");
        jpeg_destroy_decompress(&cinfo);
        return -2;
    }
    OUT_LOG("jpeg_start_decompress");
    int row_stride = 0, width = 0, height = 0, pixel_size = 0;

    jpeg_start_decompress(&cinfo);
    width = cinfo.output_width;
    height = cinfo.output_height;
    pixel_size = cinfo.output_components;

    OUT_LOG("output_width = %d, output_height = %d,output_components = %d ",
            cinfo.output_width,
            cinfo.output_height,
           cinfo.output_components);

    unsigned long bmp_size = 0;
    unsigned char *bmp_buffer = 0;
    bmp_size = width * height * pixel_size;
    bmp_buffer = new unsigned char[bmp_size];
    memset(bmp_buffer, 0, bmp_size);

    row_stride = width * pixel_size;

    while (cinfo.output_scanline < cinfo.output_height)
    {
        unsigned char *buffer_array[1];
        buffer_array[0] = bmp_buffer + (cinfo.output_scanline) * row_stride;

        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }
    OUT_LOG("finish jpeg_read_scanlines");
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);

    OUT_LOG("finish jpeg_destroy_decompress");
    *pRGBBuffer = bmp_buffer;
    *pOutSize = bmp_size;
    *outWidth = width;
    *outHeigth = height;

    return 0;
}

TOOL_SDK_API unsigned char * DLL_SPEC rbg24_Stretch_Linear( unsigned char *srcRGBData, int w_Src, int h_Src, int bit_depth, int w_Dest, int h_Dest)
{
    int sw = w_Src-1, sh = h_Src-1, dw = w_Dest-1, dh = h_Dest-1;
    int B, N, x, y;
    int nPixelSize = bit_depth/8;
    unsigned char *pLinePrev,*pLineNext;
    unsigned char *pDest = new unsigned char[w_Dest*h_Dest*bit_depth/8];
    unsigned char *tmp;
    unsigned char *pA,*pB,*pC,*pD;

    for(int i=0;i<=dh;++i)
    {
        tmp =pDest + i*w_Dest*nPixelSize;
        y = i*sh/dh;
        N = dh - i*sh%dh;
        pLinePrev = srcRGBData + (y++)*w_Src*nPixelSize;
        //pLinePrev =(unsigned char *)aSrc->m_bitBuf+((y++)*aSrc->m_width*nPixelSize);
        pLineNext = (N==dh) ? pLinePrev : srcRGBData+y*w_Src*nPixelSize;
        //pLineNext = ( N == dh ) ? pLinePrev : (unsigned char *)aSrc->m_bitBuf+(y*aSrc->m_width*nPixelSize);
        for(int j=0;j<=dw;++j)
        {
            x = j*sw/dw*nPixelSize;
            B = dw-j*sw%dw;
            pA = pLinePrev+x;
            pB = pA+nPixelSize;
            pC = pLineNext + x;
            pD = pC + nPixelSize;
            if(B == dw)
            {
                pB=pA;
                pD=pC;
            }

            for(int k=0;k<nPixelSize;++k)
            {
                *tmp++ = ( unsigned char )( int )(
                            ( B * N * ( *pA++ - *pB - *pC + *pD ) + dw * N * *pB++
                              + dh * B * *pC++ + ( dw * dh - dh * B - dw * N ) * *pD++
                              + dw * dh / 2 ) / ( dw * dh ) );
            }
        }
    }
    return pDest;
}

bool ExtractRGB24FromBMP(void* lpBmpBuffer, int iBufLen, unsigned char** ppRGBData, int* width, int* height) 
{
    if (lpBmpBuffer == nullptr || iBufLen < static_cast<int>(sizeof(bmp_fileheader) + sizeof(bmp_infoheader)))
    {
        return false;
    }

    bmp_fileheader* fileHeader = (bmp_fileheader*)lpBmpBuffer;
    bmp_infoheader* infoHeader = (bmp_infoheader*)((char*)lpBmpBuffer + sizeof(bmp_fileheader));

    // 检查文件类型是否为 BMP
    if (fileHeader->bfType != 0x4D42) 
    {
        return false;
    }

    // 仅支持 24 位无压缩的 BMP
    if (infoHeader->biBitCount != 24 || infoHeader->biCompression != 0) 
    {
        return false;
    }

    *width = infoHeader->biWidth;
    *height = std::abs((int)(infoHeader->biHeight));

    // 计算每行字节数，需要按 4 字节对齐
    int rowSize = (*width * 3 + 3) & ~3;

    int rgbDataSize = (*width) * (*height) * 3;
    *ppRGBData = new unsigned char[rgbDataSize];
    if (*ppRGBData == NULL) {
        return false;
    }

    unsigned char* bmpData = reinterpret_cast<unsigned char*>(lpBmpBuffer) + fileHeader->bfOffBits;
    for (int y = 0; y < *height; ++y) {
        int bmpRowIndex = (infoHeader->biHeight > 0 ? (*height - y - 1) : y) * rowSize;
        int rgbRowIndex = y * (*width) * 3;
        for (int x = 0; x < *width; ++x) {
            int bmpIndex = bmpRowIndex + x * 3;
            int rgbIndex = rgbRowIndex + x * 3;
            (*ppRGBData)[rgbIndex] = bmpData[bmpIndex + 2]; // R
            (*ppRGBData)[rgbIndex + 1] = bmpData[bmpIndex + 1]; // G
            (*ppRGBData)[rgbIndex + 2] = bmpData[bmpIndex]; // B
        }
    }

    return true;
}
// 将 RGB24 图像数据转换为 JPEG 数据的函数
TOOL_SDK_API int DLL_SPEC rbg24_to_jpeg(unsigned char *srcRGBData, int w_Src, int h_Src, void** ppJpegBuffer, unsigned long *pOutSize) 
{
    struct jpeg_compress_struct cinfo;
    struct my_error_mgr jerr;
    JSAMPROW row_pointer[1];
    int row_stride;
    unsigned char *jpeg_buffer = NULL;
    unsigned long jpeg_size = 0;

    // 初始化 JPEG 压缩对象
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        if (jpeg_buffer) {
            free(jpeg_buffer);
        }
        jpeg_destroy_compress(&cinfo);
        return -1;
    }
    jpeg_create_compress(&cinfo);

    // 让 libjpeg 输出到内存缓冲区
    jpeg_mem_dest(&cinfo, &jpeg_buffer, &jpeg_size);

    // 设置 JPEG 压缩参数
    cinfo.image_width = w_Src;
    cinfo.image_height = h_Src;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 50, TRUE);

    // 开始压缩
    jpeg_start_compress(&cinfo, TRUE);

    row_stride = w_Src * 3;
    while (cinfo.next_scanline < cinfo.image_height) {
        row_pointer[0] = &srcRGBData[cinfo.next_scanline * row_stride];
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    // 完成压缩
    jpeg_finish_compress(&cinfo);

    // 为 ppJpegBuffer 分配内存并拷贝数据
    // *ppJpegBuffer = malloc(jpeg_size);
    *ppJpegBuffer = new unsigned char[jpeg_size]; 
    if (*ppJpegBuffer == NULL) {
        // 内存分配失败，释放之前分配的内存
        free(jpeg_buffer);
        jpeg_destroy_compress(&cinfo);
        return -1;
    }
    memcpy(*ppJpegBuffer, jpeg_buffer, jpeg_size);

    // 释放 libjpeg 分配的临时内存
    free(jpeg_buffer);

    // 输出压缩后的 JPEG 数据大小
    *pOutSize = jpeg_size;

    // 销毁 JPEG 压缩对象
    jpeg_destroy_compress(&cinfo);

    return 0;
}

TOOL_SDK_API int DLL_SPEC Bmp_get_width_and_height(void* lpBmpBuffer,int bufSize,  int* nWidth, int* nHeight)
{
    if (lpBmpBuffer == nullptr || bufSize < static_cast<int>(sizeof(bmp_fileheader) + sizeof(bmp_infoheader)))
    {
        return false;
    }

    bmp_fileheader* fileHeader = (bmp_fileheader*)lpBmpBuffer;
    bmp_infoheader* infoHeader = (bmp_infoheader*)((char*)lpBmpBuffer + sizeof(bmp_fileheader));

    // 检查文件类型是否为 BMP
    if (fileHeader->bfType != 0x4D42) 
    {
        return -1;
    }

    // 仅支持 24 位无压缩的 BMP
    if (infoHeader->biBitCount != 24 || infoHeader->biCompression != 0) 
    {
        return -2;
    }

    *nWidth = infoHeader->biWidth;
    *nHeight = std::abs((int)(infoHeader->biHeight));
    return 0;
}

TOOL_SDK_API int DLL_SPEC Bmp_to_jpeg(void* lpBmpBuffer, int bufSize, void** ppJpegBuffer,  unsigned long* pOutSize)
{
    if(NULL == lpBmpBuffer)
    {
        OUT_LOG("NULL == lpBmpBuffer or parameter is invalid.");
        return -1;
    }
    unsigned char* srcRGBData = NULL;
    int iWidth = 0;
    int iHeight = 0;
    if (!ExtractRGB24FromBMP(lpBmpBuffer, bufSize, &srcRGBData, &iWidth, &iHeight))
    {
        return -1;
    }

    int iRet = rbg24_to_jpeg(srcRGBData, iWidth, iHeight, ppJpegBuffer, pOutSize);
    delete[] srcRGBData;
    srcRGBData = NULL;
    return iRet;
}
// 转换 JPG 为 BMP 图的函数
TOOL_SDK_API int DLL_SPEC jpeg_decompress_to_bmp(unsigned char *pjpgBuffer, int jpgSize, void **pBmpBuffer, unsigned long *pOutSize, int *outWidth, int *outHeigth) 
{
    struct jpeg_decompress_struct dinfo;
    struct my_error_mgr jerr;
    JSAMPARRAY buffer;
    int row_stride;
    unsigned char *bmpBuffer = NULL;
    unsigned long bmpSize = 0;

    // 初始化 JPEG 解码对象
    dinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&dinfo);
        if (bmpBuffer) {
            free(bmpBuffer);
        }
        return -1;
    }
    jpeg_create_decompress(&dinfo);
    jpeg_mem_src(&dinfo, pjpgBuffer, (size_t)jpgSize);
    jpeg_read_header(&dinfo, TRUE);
    jpeg_start_decompress(&dinfo);

    // 获取图像宽高
    *outWidth = dinfo.output_width;
    *outHeigth = dinfo.output_height;

    // 计算每行字节数，需要 4 字节对齐
    int rowSize = (*outWidth * 3 + 3) & ~3;
    bmpSize = sizeof(bmp_fileheader) + sizeof(bmp_infoheader) + rowSize * (*outHeigth);

    // 分配 BMP 缓冲区内存
    bmpBuffer = (unsigned char *)malloc(bmpSize);
    if (bmpBuffer == NULL) {
        jpeg_destroy_decompress(&dinfo);
        return -1;
    }

    // 填充 BMP 文件头
    bmp_fileheader *fileHeader = (bmp_fileheader *)bmpBuffer;
    char bfType[2] = { 'B', 'M' };
    memcpy(fileHeader, bfType, sizeof(bfType));
    // fileHeader->bfType[0] = 'B';
    // fileHeader->bfType[1] = 'M';
    fileHeader->bfSize = (unsigned int)bmpSize;
    fileHeader->bfReverved1 = 0;
    fileHeader->bfReverved2 = 0;
    fileHeader->bfOffBits = sizeof(bmp_fileheader) + sizeof(bmp_infoheader);

    // 填充 BMP 信息头
    bmp_infoheader *infoHeader = (bmp_infoheader *)(bmpBuffer + sizeof(bmp_fileheader));
    infoHeader->biSize = sizeof(bmp_infoheader);
    infoHeader->biWidth = *outWidth;
    infoHeader->biHeight = *outHeigth;
    infoHeader->biPlanes = 1;
    infoHeader->biBitCount = 24;
    infoHeader->biCompression = 0;
    infoHeader->biSizeImage = rowSize * (*outHeigth);
    infoHeader->biXPelsPerMeter = 0;
    infoHeader->biYpelsPerMeter = 0;
    infoHeader->biClrUsed = 0;
    infoHeader->biClrImportant = 0;

    // 分配内存用于存储解码后的图像数据
    row_stride = dinfo.output_width * dinfo.output_components;
    buffer = (*dinfo.mem->alloc_sarray)((j_common_ptr)&dinfo, JPOOL_IMAGE, row_stride, 1);

    // 从 JPEG 解码并写入 BMP 数据
    unsigned char *bmpData = bmpBuffer + sizeof(bmp_fileheader) + sizeof(bmp_infoheader);
    for (int y = *outHeigth - 1; y >= 0; y--) {
        jpeg_read_scanlines(&dinfo, buffer, 1);
        for (int x = 0; x < *outWidth; x++) {
            // BMP 是 BGR 格式，JPEG 是 RGB 格式，需要交换通道
            bmpData[y * rowSize + x * 3 + 0] = buffer[0][x * 3 + 2];
            bmpData[y * rowSize + x * 3 + 1] = buffer[0][x * 3 + 1];
            bmpData[y * rowSize + x * 3 + 2] = buffer[0][x * 3 + 0];
        }
    }

    // 完成解码
    jpeg_finish_decompress(&dinfo);

    // 输出结果
    *pBmpBuffer = bmpBuffer;
    *pOutSize = bmpSize;

    // 销毁对象
    jpeg_destroy_decompress(&dinfo);

    return 0;
}

TOOL_SDK_API int DLL_SPEC jpeg_resize_to_bmp(unsigned char *pjpgBuffer, int jpgSize, int destW, int destH, void **pBmpBuffer, unsigned long *pOutSize)
{
    if(NULL == pjpgBuffer
            || jpgSize <= 0
            || destW < 8
            || destH < 8
            || pOutSize == NULL)
    {
        OUT_LOG("parameter is invalid");
        return -1;
    }

    if (pjpgBuffer[0] != 0xFFu || pjpgBuffer[1] != 0xD8u)
    {
        OUT_LOG("Bad jpeg start with: %#x%x", pjpgBuffer[0], pjpgBuffer[1]);
        return -1;
    }

    unsigned long rgb24_size = 0;
    unsigned char *rgb24_buffer = NULL;
    int src_width = 0;
    int src_height = 0;
    int src_pixel_size = 3;

    if(0 != jpeg_decompress_to_rgb24(pjpgBuffer, jpgSize,(void**)&rgb24_buffer, &rgb24_size, &src_width, &src_height)
            || rgb24_buffer == NULL
            || rgb24_size <= 0)
    {
        OUT_LOG("decompress to rgb24 failed.");
        return -2;
    }
    OUT_LOG("src_width = %d, src_height = %d,rgb data = %p,  size = %d ",
            src_width,
            src_height,
            rgb24_buffer,
            rgb24_size);

    //strech rgb data
    int iDestW = destW;
    int iDestH = destH;
    int iStrechSize = iDestW*iDestH*3;
    OUT_LOG("destW = %d, dest H = %d, strechSize = %d", iDestW,iDestH, iStrechSize );

    unsigned char *pStrechData =  rbg24_Stretch_Linear(rgb24_buffer, src_width, src_height,  src_pixel_size * 8, iDestW,  iDestH );

    unsigned char *destOutBuf = new unsigned char[iStrechSize*2];
    int iBufferLength = iStrechSize*2;

    simplest_bgr24_to_bmp(pStrechData, iStrechSize, iDestW, iDestH, destOutBuf, &iBufferLength);

    *pBmpBuffer = destOutBuf;
    *pOutSize = iBufferLength;

    if(pStrechData)
    {
        delete[] pStrechData;
        pStrechData = NULL;
    }

    if(rgb24_buffer)
    {
        delete[] rgb24_buffer;
        rgb24_buffer = NULL;
    }

    return 0;
}

// 双线性插值函数
unsigned char bilinear_interpolation(unsigned char *src, int src_w, int src_h, float x, float y, int channel) 
{
    int x0 = (int)x;
    int x1 = (x0 < src_w - 1) ? x0 + 1 : x0;
    int y0 = (int)y;
    int y1 = (y0 < src_h - 1) ? y0 + 1 : y0;

    float dx = x - x0;
    float dy = y - y0;

    unsigned char c00 = src[(y0 * src_w + x0) * 3 + channel];
    unsigned char c01 = src[(y0 * src_w + x1) * 3 + channel];
    unsigned char c10 = src[(y1 * src_w + x0) * 3 + channel];
    unsigned char c11 = src[(y1 * src_w + x1) * 3 + channel];

    return (unsigned char)((1 - dx) * (1 - dy) * c00 + dx * (1 - dy) * c01 + (1 - dx) * dy * c10 + dx * dy * c11);
}


// 对 JPEG 图像进行大小缩放的函数
TOOL_SDK_API int DLL_SPEC jpeg_resize(unsigned char *pjpgBuffer, int jpgSize, int destW, int destH, void **pOutBuffer, unsigned long *pOutSize) 
{
    if(NULL == pjpgBuffer
            || jpgSize <= 0
            || destW < 8
            || destH < 8)
    {
        OUT_LOG("parameter is invalid\n");
        return -1;
    }
    struct jpeg_decompress_struct dinfo;
    struct jpeg_compress_struct cinfo;
    struct my_error_mgr jerr;
    JSAMPARRAY buffer;
    int row_stride;
    unsigned char *tempJpegBuffer = NULL;
    unsigned long tempJpegSize = 0;
    unsigned char *finalOutBuffer = NULL;
    unsigned char *srcImage = NULL;

    // 初始化 JPEG 解码对象
    dinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&dinfo);
        if (tempJpegBuffer) {
            free(tempJpegBuffer);
        }
        if (finalOutBuffer) {
            free(finalOutBuffer);
        }
        if (srcImage) {
            free(srcImage);
        }
        OUT_LOG("setjmp failed\n");
        return -2;
    }
    jpeg_create_decompress(&dinfo);
    jpeg_mem_src(&dinfo, pjpgBuffer, (size_t)jpgSize);
    jpeg_read_header(&dinfo, TRUE);
    jpeg_start_decompress(&dinfo);

    // 分配内存用于存储解码后的图像数据
    row_stride = dinfo.output_width * dinfo.output_components;
    buffer = (*dinfo.mem->alloc_sarray)((j_common_ptr)&dinfo, JPOOL_IMAGE, row_stride, 1);
    srcImage = (unsigned char *)malloc(dinfo.output_height * row_stride);
    OUT_LOG("alloc srcImage(%u,%u) malloc(%u*%d)= %u", 
        dinfo.output_width , 
        dinfo.output_height,
        dinfo.output_height ,
        row_stride,
        dinfo.output_height * row_stride);

    if (srcImage == NULL) {
        jpeg_destroy_decompress(&dinfo);
        OUT_LOG("malloc srcImage failed\n");
        return -3;
    }
    for (int i = 0; i < dinfo.output_height; i++) {
        jpeg_read_scanlines(&dinfo, buffer, 1);
        memcpy(srcImage + i * row_stride, buffer[0], row_stride);
    }
    jpeg_finish_decompress(&dinfo);

    // 初始化 JPEG 编码对象
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        if (srcImage) {
            free(srcImage);
        }
        jpeg_destroy_compress(&cinfo);
        if (tempJpegBuffer) {
            free(tempJpegBuffer);
        }
        if (finalOutBuffer) {
            free(finalOutBuffer);
        }
        return -4;
    }
    jpeg_create_compress(&cinfo);
    jpeg_mem_dest(&cinfo, &tempJpegBuffer, &tempJpegSize);

    // 设置编码参数
    cinfo.image_width = destW;
    cinfo.image_height = destH;
    cinfo.input_components = dinfo.output_components;
    cinfo.in_color_space = dinfo.out_color_space;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 50, TRUE);

    // 开始编码
    jpeg_start_compress(&cinfo, TRUE);

    // 分配内存用于存储缩放后的图像数据
    unsigned char *destImage = (unsigned char *)malloc(destH * destW * 3);
    if (destImage == NULL) {
        if (srcImage) {
            free(srcImage);
        }
        jpeg_destroy_compress(&cinfo);
        if (tempJpegBuffer) {
            free(tempJpegBuffer);
        }
        return -5;
    }

    // 进行缩放
    for (int y = 0; y < destH; y++) {
        for (int x = 0; x < destW; x++) {
            float src_x = (float)x * dinfo.output_width / destW;
            float src_y = (float)y * dinfo.output_height / destH;
            for (int c = 0; c < 3; c++) {
                destImage[(y * destW + x) * 3 + c] = bilinear_interpolation(srcImage, dinfo.output_width, dinfo.output_height, src_x, src_y, c);
            }
        }
    }

    // 写入缩放后的图像数据
    for (int y = 0; y < destH; y++) {
        buffer[0] = destImage + y * destW * 3;
        jpeg_write_scanlines(&cinfo, buffer, 1);
    }

    // 完成编码
    jpeg_finish_compress(&cinfo);

    // 为 pOutBuffer 分配内存并拷贝数据
    // finalOutBuffer = (unsigned char *)malloc(tempJpegSize);
    finalOutBuffer = new unsigned char[tempJpegSize];
    if (finalOutBuffer == NULL) {
        // 内存分配失败，释放之前分配的内存
        free(tempJpegBuffer);
        free(destImage);
        if (srcImage) {
            free(srcImage);
        }
        jpeg_destroy_compress(&cinfo);
        return -6;
    }
    memcpy(finalOutBuffer, tempJpegBuffer, tempJpegSize);

    // 释放临时缓冲区
    free(tempJpegBuffer);
    free(destImage);
    if (srcImage) {
        free(srcImage);
    }

    // 输出结果
    *pOutBuffer = finalOutBuffer;
    *pOutSize = tempJpegSize;

    // 销毁对象
    jpeg_destroy_compress(&cinfo);

    return 0;
}

TOOL_SDK_API int DLL_SPEC bmp_resize(unsigned char *srcBmp, int srcSize, int srcW, int srcH, void **pOutBuffer, unsigned long *pOutSize, int destW, int destH)
{
    unsigned char * srcRgbData = srcBmp + 54;

    unsigned char *strechData = rbg24_Stretch_Linear(srcRgbData, srcW, srcH, 24, destW,destH );
    if(NULL == strechData)
    {
        OUT_LOG("rbg24_Stretch_Linear from (%d, %d) to (%d, %d) failed.",srcW, srcH, destW,destH);
        return -1;
    }
    int iStrechDataLen = srcW * srcH * 3;

    unsigned char *bmpBuf = new unsigned char[iStrechDataLen*2];
    int iBmpBufLen = iStrechDataLen*2;
    int iRet = simplest_bgr24_to_bmp(strechData, iStrechDataLen, srcW, srcH, bmpBuf, &iBmpBufLen);
    if(iRet == 0)
    {
        OUT_LOG("simplest_bgr24_to_bmp success, size = %d.",iBmpBufLen);

        *pOutBuffer = bmpBuf;
        *pOutSize = iBmpBufLen;
    }
    else
    {
        OUT_LOG("simplest_bgr24_to_bmp failed.");
        if(bmpBuf)
        {
            delete[] bmpBuf;
            bmpBuf = NULL;
        }
    }

    if(strechData)
    {
        delete[] strechData;
        strechData = NULL;
    }

    return iRet;
}

unsigned char* ConvertRGBToGray(unsigned char* rgbData, int width, int height) 
{
    unsigned char* grayData = new unsigned char[width * height];
    if (grayData == NULL) 
    {
        return NULL;
    }

    for (int y = 0; y < height; ++y) 
    {
        for (int x = 0; x < width; ++x) 
        {
            int rgbIndex = (y * width + x) * 3;
            int grayIndex = y * width + x;
            // 使用公式计算灰度值：Gray = 0.299*R + 0.587*G + 0.114*B
            grayData[grayIndex] = static_cast<unsigned char>(
                0.299 * rgbData[rgbIndex] + 0.587 * rgbData[rgbIndex + 1] + 0.114 * rgbData[rgbIndex + 2]);
        }
    }

    return grayData;
}

TOOL_SDK_API int DLL_SPEC Bmp_to_jpeg_gray(void *lpBmpBuffer, int bufSize, unsigned char **ppJpegBuffer, unsigned long *pOutSize)
{
    if(NULL == lpBmpBuffer
            || pOutSize == NULL)
    {
        OUT_LOG("parameter is invalid.");
        return -1;
    }

    struct bmp_fileheader bfh;
    struct bmp_infoheader bih;

    memcpy(&bfh, (unsigned char*)lpBmpBuffer, sizeof(bmp_fileheader));
    memcpy(&bih, (unsigned char*)lpBmpBuffer+ sizeof(bmp_fileheader) , sizeof(bmp_infoheader));

    OUT_LOG("parse bmp file header: bfType = %x, bfSize = %u, bfReverved1= %x, bfReverved2 = %x, bfOffBits = %u ",
            bfh.bfType,
            bfh.bfSize,
            bfh.bfReverved1,
            bfh.bfReverved2,
            bfh.bfOffBits
            );
    OUT_LOG("bmp info header: biSize = %u ",bih.biSize);
    OUT_LOG("bmp info header: biWidth = %u ",bih.biWidth);
    OUT_LOG("bmp info header: biHeight = %u ",bih.biHeight);
    OUT_LOG("bmp info header: biPlanes = %u ",bih.biPlanes);
    OUT_LOG("bmp info header: biBitCount = %u ",bih.biBitCount);
    OUT_LOG("bmp info header: biCompression = %u ",bih.biCompression);
    OUT_LOG("bmp info header: biSizeImage = %u ",bih.biSizeImage);
    OUT_LOG("bmp info header: biXPelsPerMeter = %u ",bih.biXPelsPerMeter);
    OUT_LOG("bmp info header: biYpelsPerMeter = %u ",bih.biYpelsPerMeter);
    OUT_LOG("bmp info header: biClrUsed = %u ",bih.biClrUsed);
    OUT_LOG("bmp info header: biClrImportant = %u ",bih.biClrImportant);


    unsigned char* rgbData = NULL;
    int width, height;
    // 从 BMP 数据中提取 RGB 数据
    if (!ExtractRGB24FromBMP(lpBmpBuffer, bufSize, &rgbData, &width, &height)) {
        return -1;
    }
   // 将 RGB 数据转换为灰度数据
   unsigned char* grayData = ConvertRGBToGray(rgbData, width, height);
   if (grayData == NULL) {
       delete[] rgbData;
       return -1;
   }

    // 初始化 JPEG 压缩对象
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    // 设置 JPEG 输出为内存缓冲区
    unsigned char* jpegBuffer = nullptr;
    unsigned long jpegSize = 0;
    jpeg_mem_dest(&cinfo, &jpegBuffer, &jpegSize);

    // 设置 JPEG 压缩参数
    cinfo.image_width = width;
    cinfo.image_height = height;
    cinfo.input_components = 1; // 灰度图像只有 1 个通道
    cinfo.in_color_space = JCS_GRAYSCALE;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 50, TRUE); // 设置压缩质量为 50

    // 开始 JPEG 压缩过程
    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW row_pointer[1];
    for (int y = 0; y < height; ++y) {
        row_pointer[0] = &grayData[y * width];
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    // 完成 JPEG 压缩
    jpeg_finish_compress(&cinfo);

    // 释放 JPEG 压缩对象
    jpeg_destroy_compress(&cinfo);

    // 返回 JPEG 数据和大小
    // *ppJpegBuffer = jpegBuffer;
    *ppJpegBuffer = new unsigned char[jpegSize];
    memcpy(*ppJpegBuffer, jpegBuffer, jpegSize);    
    *pOutSize = jpegSize;

    // 释放提取的 RGB 数据和灰度数据
    delete[] rgbData;
    delete[] grayData;
    free(jpegBuffer);

    return 0;
}

TOOL_SDK_API int DLL_SPEC libjpeg_xlwbin_to_jpeg(unsigned char *pBinData, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    cinfo.image_width = width;  /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.input_components = 3; /* # of color components per pixel */
    cinfo.in_color_space = JCS_YCbCr; /* colorspace of input image */

    unsigned char *pjpegData = NULL;
    unsigned long outLength;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 70, TRUE);
    jpeg_mem_dest(&cinfo, &pjpegData, &outLength);

    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW jrow[1];    /* pointer to a single row */
    unsigned char *buf = new unsigned char[width * 3];

    for(unsigned int j=0;j<cinfo.image_height;j++ )
    {
        for (unsigned int i = 0; i < cinfo.image_width; i++)
        {
            unsigned char cy = pBinData[ (j * cinfo.image_width + i) >> 3 ];
            int r = (j * cinfo.image_width + i) & 0x07;
            unsigned char cy2 = (( cy & (0x01 << r)) >> r) ? 255 : 0;

            buf[i*3] = cy2;
            buf[i*3+1] = 0x80;
            buf[i*3+2] = 0x80;
        }

        jrow[0] = buf;
        jpeg_write_scanlines(&cinfo, jrow, 1);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    if(outLength != 0)
    {
        *ppJpegData = new unsigned char[outLength];
        memcpy(*ppJpegData, pjpegData, outLength);
        *nJpegLen = outLength;

        free(pjpegData);
        pjpegData = NULL;
    }
    else
    {
        *nJpegLen = 0;
    }

    delete[] buf;
    buf = NULL;

    return 0;
}

TOOL_SDK_API int DLL_SPEC libjpeg_yuv422_to_jpeg(unsigned char *pYUV422Data, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    if(NULL == pYUV422Data)
    {
        OUT_LOG("parameter is invalid, pYUV422Data == NULL.");
        return -1;
    }

    unsigned char *buf = new unsigned char[width * 3];
    if(buf == NULL)
    {
        OUT_LOG("malloc buf failed.");
        return -2;
    }

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    cinfo.image_width = width;  /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.input_components = 3; /* # of color components per pixel */
    cinfo.in_color_space = JCS_YCbCr; /* colorspace of input image */

    unsigned char *pjpegData = NULL;
    unsigned long outLength;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 50, TRUE);
    jpeg_mem_dest(&cinfo, &pjpegData, &outLength);

    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW jrow[1];    /* pointer to a single row */

    unsigned char* pY = pYUV422Data;
    unsigned char* pU = pYUV422Data + cinfo.image_width*cinfo.image_height;
    unsigned char* pV = pU + (cinfo.image_width /2) * cinfo.image_height;
    while (cinfo.next_scanline < cinfo.image_height)
    {
        for (unsigned int i = 0; i < cinfo.image_width; i++)
        {
            buf[i*3] = pY[i];
            buf[i*3+1] = pU[i/2];
            buf[i*3+2] = pV[i/2];
        }
        pY += cinfo.image_width;
        pU += cinfo.image_width / 2;
        pV += cinfo.image_width / 2;

        jrow[0] = buf;
        jpeg_write_scanlines(&cinfo, jrow, 1);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    int iRet = 0;
    if(outLength != 0)
    {
        *ppJpegData = new unsigned char[outLength];
        if(NULL == *ppJpegData)
        {
            OUT_LOG("malloc jpeg data failed.");
            iRet = -3;
        }
        else
        {
            memcpy(*ppJpegData, pjpegData, outLength);
            *nJpegLen = outLength;
        }
        free(pjpegData);
        pjpegData = NULL;
    }
    else
    {
        *nJpegLen = 0;
    }

    delete[] buf;
    buf = NULL;

    return 0;
}

#endif


#ifdef USE_TURBO_JPEG

TOOL_SDK_API int DLL_SPEC turbo_yuv422_to_jpg(unsigned char *pYUV422Data, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    tjhandle hProcess =  tjInitCompress();

    int flags = TJ_YUV;
    int padding = 1; // 1�??4均可，但不能�??0
    int need_size = 0;
    int ret = 0;
    int subsamp = TJSAMP_422;

    need_size = tjBufSizeYUV2(width, padding, height, TJSAMP_422);

    unsigned char *pJpegData = NULL;
    unsigned long OutLen = 0;

    unsigned char *srcPlanes[3];
    int strides[3], retval = -1;

    if (pYUV422Data == NULL
            || width <= 0
            || padding < 1
            || height <= 0
            || subsamp < 0
            || subsamp >= NUMSUBOPT)
    {
        printf("tjCompressFromYUV(): Invalid argument \n");
        return 1;
    }

    srcPlanes[0] = pYUV422Data;
    srcPlanes[1] = pYUV422Data + width*height;
    srcPlanes[2] = srcPlanes[1] + (width / 2) * height;

    strides[0] = width;
    strides[1] = width / 2;
    strides[2] = width / 2;

    // The original small image YUV data of xin_lu_wei is not aligned , so the tjCompressFromYUV interface cannot be used directly
    ret=  tjCompressFromYUVPlanes(
                hProcess,
                srcPlanes,
                width,
                strides,
                height,
                subsamp,
                &pJpegData,
                &OutLen,
                50,
                flags);

    if (ret < 0)
    {
        printf("compress to jpeg failed: %s\n", tjGetErrorStr());
    }
    else
    {
        //OUT_LOG("tjCompressFromYUV = %d, OutLen = %lu", ret, OutLen);
        *ppJpegData = new unsigned char[OutLen];
        memcpy(*ppJpegData, pJpegData, OutLen);
        *nJpegLen = OutLen;

        //free(pJpegData);
        //pJpegData = NULL;
        tjFree(pJpegData);
    }

    tjDestroy(hProcess);
    return ret;

bailout:
    return retval;
}

TOOL_SDK_API int DLL_SPEC turbo_rbg24_to_jpeg(unsigned char *srcRGBData, int w_Src, int h_Src, unsigned char** ppJpegBuffer, unsigned long* pOutSize)
{
    tjhandle pJpgProcessor =  tjInitCompress();

    long unsigned int iOutjpegSize = 0;
    unsigned char* pCompressedImage = NULL; //!< Memory is allocated by tjCompress2 if _jpegSize == 0
    int iRet = tjCompress2(pJpgProcessor,
        srcRGBData,
        w_Src,
        0,
        h_Src,
        TJPF_BGR,
        &pCompressedImage,
        &iOutjpegSize,
        TJSAMP_444, 70, TJFLAG_FASTDCT);
    tjDestroy(pJpgProcessor);

    if (iOutjpegSize != 0 && pCompressedImage != NULL)
    {
        *ppJpegBuffer = new unsigned char[iOutjpegSize];
        memcpy(*ppJpegBuffer, pCompressedImage, iOutjpegSize);
        *pOutSize = iOutjpegSize;
    }
    else
    {
        *pOutSize = 0;
    }
    tjFree(pCompressedImage);

    return iRet;
}

TOOL_SDK_API int DLL_SPEC turbo_bmp_to_jpg(void* lpBmpBuffer, int nWidth, int nHeight, unsigned char** ppJpegBuffer, unsigned long* pOutSize)
{
    if (NULL == lpBmpBuffer
        || 0 == nWidth
        || 0 == nHeight)
    {
        OUT_LOG("NULL == lpBmpBuffer or parameter is invalid.");
        return -1;
    }
    struct bmp_fileheader bfh;
    struct bmp_infoheader bih;

    memcpy(&bfh, (unsigned char*)lpBmpBuffer, sizeof(bmp_fileheader));
    memcpy(&bih, (unsigned char*)lpBmpBuffer + sizeof(bmp_fileheader), sizeof(bmp_infoheader));

    if (bih.biBitCount == 24)
    {
        unsigned char *srcRGBData = (unsigned char *)lpBmpBuffer + bfh.bfOffBits;
        return turbo_rbg24_to_jpeg(srcRGBData, nWidth, nHeight, ppJpegBuffer, pOutSize);
    }
    else
    {
        OUT_LOG("bmp bitcount (%u) != 24, return -1", bih.biBitCount);
        return -2;
    }
}

TOOL_SDK_API int DLL_SPEC turbo_xlw_bin_to_jpg(unsigned char *pBinaryData, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    if (NULL == pBinaryData
        || 0 == width
        || 0 == height)
    {
        OUT_LOG("NULL == lpBmpBuffer or parameter is invalid.");
        return -1;
    }

    //The original data only has "Y" information, and 8 pixels are compressed into 1, so we need to expand it and fill in the "U" and "V" data
    unsigned char *pYUVbuf = new unsigned char[height* width * 3];
    if(pYUVbuf == NULL)
    {
        OUT_LOG("NULL == buf.");
        return -1;
    }
    memset(pYUVbuf, 0x80, height* width * 3);       //filling gray color

    for( int j=0;j<height;j++ )
    {
        for (int i = 0; i < width; i++)
        {
            unsigned char cy = pBinaryData[ (j * width + i) >> 3 ];
            int r = (j * width + i) & 0x07;
            unsigned char cy2 = (( cy & (0x01 << r)) >> r) ? 255 : 0;

            //filling as yuv 444 packed
//            buf[j*width*3 + i*3] = cy2;
//            buf[j*width*3 + i*3+1] = 0x80;
//            buf[j*width*3 + i*3+2] = 0x80;

            //filling as yuv 444 planar
             pYUVbuf[j*width + i] = cy2;
             pYUVbuf[height* width+ j*width + i] = 0x80;
             pYUVbuf[2*height* width+ j*width + i] = 0x80;
        }
    }

    tjhandle hProcess =  tjInitCompress();

    int flags = TJ_YUV;
    int padding = 1; // 1�??4均可，但不能�??0
    int need_size = 0;
    int ret = 0;
    int subsamp = TJSAMP_444;

    need_size = tjBufSizeYUV2(width, padding, height, TJSAMP_444);

    unsigned char *pJpegData = NULL;
    unsigned long OutLen = 0;

    unsigned char* pSrcYUVData = pYUVbuf;

    //tjCompressFromYUV only support YUV planar compress
    ret=  tjCompressFromYUV(
                hProcess,
                pSrcYUVData,
                width,
                padding,
                height,
                subsamp,
                &pJpegData,
                &OutLen,
                100,
                flags);

    delete[] pYUVbuf;
    pYUVbuf = NULL;

    if (ret < 0)
    {
        printf("compress to jpeg failed: %s\n", tjGetErrorStr());
    }
    else
    {
        *ppJpegData = new unsigned char[OutLen];
        memcpy(*ppJpegData, pJpegData, OutLen);
        *nJpegLen = OutLen;

        tjFree(pJpegData);
    }

    tjDestroy(hProcess);
    return ret;
}

#endif

}   //namespace
